package com.luke.nio.demo;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * NIO是一个同步非阻塞IO框架
 * AIO是一个异步非阻塞IO框架
 * Selector 包含多个SelectionKey，一个SelectionKey包含一个Channel（SeverSocketChannel或者SocketChannel）
 * Channel可以注册到多个Selector上，即Selector可以有多个
 */
public class NIOServer {

    public static void main(String[] args) throws Exception{

        //创建ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //监听端口
        serverSocketChannel.socket().bind(new InetSocketAddress(8762));
        serverSocketChannel.configureBlocking(false);//非阻塞

        //创建一个选择器（多路复用器）Selector
        //Selector管理SelectionKey集合，一个SelectionKey对应一个Channel
        Selector selector = Selector.open();

        //将ServerSocketChannel注册到Selector
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//监听连接事件

        System.out.println("ServerSocketChannel注册后的selectionKey数量="+selector.keys().size());

        while (true){
            //等待1秒钟，如果没有事件可以执行其他操作
            if(selector.select(1000) == 0){//或者selector.selectNow() 体现了NIO的非阻塞
                System.out.println("服务端等待客户端连接");
                continue;
            }
            //获取有事件发生的SelectionKey集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            System.out.println("selectionKeys="+selectionKeys.size()); //有事件发生的selectionKey

            //遍历所有选择出的SelectionKey集合
            Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();
            while (selectionKeyIterator.hasNext()){
                SelectionKey selectionKey = selectionKeyIterator.next();
                //selectionKey.readyOps()
                System.out.println("selectionKey hashCode:"+selectionKey.hashCode());

                //基于事件驱动，但是需要自己去主动查询是发生什么事件，这里体现了NIO的同步性

                //根据事件类型执行不同操作
                if(selectionKey.isAcceptable()){ //OP_ACCEPT 发生，表示有客户端请求连接
                    //虽然该方法是阻塞的，但是这里是在事件发生后才执行的，所以几乎不发生阻塞
                    SocketChannel socketChannel = serverSocketChannel.accept();//获取到客户端连接通道
                    socketChannel.configureBlocking(false);//非阻塞

                    //将客户端连接通道也注册到Selector，监听OP_READ事件
                    //同时为该通道绑定一个缓冲区ByteBuffer
                    socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                    System.out.println("客户端连接后的selectionKey数量="+selector.keys().size());
                }

                if(selectionKey.isReadable()){//发生OP_READ事件
                    SocketChannel channel = (SocketChannel)selectionKey.channel();//获取SelectionKey对应的Channel，即事件来自于谁
                    ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();//获取Channel对应的缓冲区

                    //从通道中读取数据（客户端发送过来的数据），写入到缓冲区ByteBuffer
                    channel.read(byteBuffer);
                    //将缓冲区数据答应出来
                    System.out.println("客户端数据："+new String(byteBuffer.array()));
                }

                //从集合selectionKeyIterator中删除该SelectionKey
                selectionKeyIterator.remove();
            }
        }
    }

}
